package com.slt.developkitweb.crm;


import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

/**
 * 　　* @description:  国密 Sm2 加密 非对称加密
 * 　　* @author chenyuwei
 * 　　* @date 2023/12/10 12:24
 */
public class GmwSm2Utils {

    private static final String STD_NAME = "sm2p256v1";
    private static final String ALGORITHM = "EC";

//
//    public static void main(String[] args) throws Exception {
//        System.out.println("======  sm2utils test  ======");
//
//        String M = "肯德基疯狂星期四 v我50";
//        System.out.println("mingwen \t" + M);
//
//        System.out.println("begin 开始生成密钥对>>>");
//        KeyPair keyPair = geneSM2KeyPair();
//
//        PublicKey publicKey = keyPair.getPublic();
//        String pubKeyHexString = getPubKeyHexString(publicKey);
//        System.out.println("publicKey\t" + pubKeyHexString);
//
//        PrivateKey privateKey = keyPair.getPrivate();
//        String priKeyHexString = getPriKeyHexString(privateKey);
//        System.out.println("privateKey\t" + priKeyHexString);
//        System.out.println("end   结束生成密钥对>>>");
//
//        System.out.println("begin sm2加密>>>");
//        String cipherData = encrypt(M, pubKeyHexString);
//        System.out.println("miwen\t" + cipherData);
//
//        System.out.println("begin sm2解密>>>");
//        String text = decrypt(cipherData, priKeyHexString);
//        System.out.println("jiemi\t" + text);
//
//        System.out.println("begin sm2签名>>>");
//        String sign = sign(M, priKeyHexString);
//        System.out.println("signvalue\t" + sign);
//
//        System.out.println("begin sm2验签>>>");
//        boolean verifyResult = verify(M, sign, pubKeyHexString);
//        System.out.println("verifyResult\t" + verifyResult);
//
//    }

    //生成 SM2 公私钥对
    public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec(STD_NAME);
        // 获取一个椭圆曲线类型的密钥对生成器
        final KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM, new BouncyCastleProvider());
        // 产生随机数
        SecureRandom secureRandom = new SecureRandom();
        // 使用SM2参数初始化生成器
        kpg.initialize(sm2Spec, secureRandom);
        // 获取密钥对
        return kpg.generateKeyPair();
    }

    //获取私钥（16进制字符串，头部不带00长度共64）
    public static String getPriKeyHexString(PrivateKey privateKey) {
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        BigInteger intPrivateKey = key.getD();
        return intPrivateKey.toString(16);
    }

    //获取私钥 base64字符串
    public static String getPriKeyBase64String(PrivateKey privateKey) {
        return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
    }

    //获取公钥（16进制字符串，头部带04长度共130）
    public static String getPubKeyHexString(PublicKey publicKey) {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return Hex.toHexString(key.getQ().getEncoded(false));
    }

    //获取公钥 base64字符串
    public static String getPubKeyBase64String(PublicKey publicKey) {
        return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
    }

    //SM2加密算法 明文数据 公钥
    public static String encrypt(String data, PublicKey publicKey) throws InvalidCipherTextException {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
    }

    //SM2加密算法 明文数据  公钥
    public static String encrypt(byte[] data, PublicKey publicKey) throws InvalidCipherTextException {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
    }

    //SM2加密算法  明文数据 公钥（16进制字符串）
    public static String encrypt(String data, String pubKeyHexString) throws InvalidCipherTextException {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
    }

    //SM2加密算法 明文数据 公钥（16进制字符串）
    public static String encrypt(byte[] data, String pubKeyHexString) throws InvalidCipherTextException {
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(STD_NAME);
        // 构造ECC算法参数，曲线方程、椭圆曲线G点、大整数N
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
        // 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为加密模式
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);

        return Hex.toHexString(arrayOfBytes);

    }

    //SM2解密算法 密文数据 私钥
    public static String decrypt(String cipherData, PrivateKey privateKey) throws InvalidCipherTextException {
        return decrypt(Hex.decode(cipherData), privateKey);
    }

    //SM2解密算法 密文数据 私钥
    public static String decrypt(byte[] cipherData, PrivateKey privateKey) throws InvalidCipherTextException {
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
    }

    //SM2解密算法 密文数据 私钥（16进制字符串）
    public static String decrypt(String cipherData, String priKeyHexString) throws InvalidCipherTextException {
        // 使用BC库加解密时密文以04开头，传入的密文前面没有04则补上
        if (!cipherData.startsWith("04")) {
            cipherData = "04" + cipherData;
        }
        return decrypt(Hex.decode(cipherData), priKeyHexString);
    }

    //SM2解密算法 密文数据 私钥（16进制字符串）
    public static String decrypt(byte[] cipherData, String priKeyHexString) throws InvalidCipherTextException {
        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(STD_NAME);
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为解密模式
        sm2Engine.init(false, privateKeyParameters);

        byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
        return new String(arrayOfBytes);

    }

    // 签名 私钥（16进制字符串）
    public static String sign(String data, String priKeyHexString) throws Exception {
        return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
    }

    // 签名 私钥（16进制字符串）
    public static String sign(byte[] data, String priKeyHexString) throws Exception {
        String signValue = null;
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(STD_NAME);
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
        signer.init(true, param);
        signer.update(data, 0, data.length);
        signValue = Hex.toHexString(signer.generateSignature());
        return signValue;
    }

    // 验签 私钥（16进制字符串）
    public static boolean verify(String data, String signValue, String publicKeyHexString){
        return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
    }

    // 验签 私钥（16进制字符串）
    public static boolean verify(byte[] data, byte[] sign, String publicKeyHexString){
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName(STD_NAME);
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        if (publicKeyHexString.length() == 128) {
            publicKeyHexString = "04" + publicKeyHexString;
        }
        ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHexString));
        CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
        signer.init(false, param);
        signer.update(data, 0, data.length);
        return signer.verifySignature(sign);
    }


}
